1 Functions tidytuesday

1.1 options & settings

options(scipen = 999)

1.3 install libs

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6     ✔ purrr   0.3.4
✔ tibble  3.1.7     ✔ dplyr   1.0.9
✔ tidyr   1.2.0     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

1.4 load data

animal_outcomes <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-21/animal_outcomes.csv')
Rows: 664 Columns: 12
── Column specification ────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): animal_type, outcome
dbl (10): year, ACT, NSW, NT, QLD, SA, TAS, VIC, WA, Total

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
animal_complaints <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-21/animal_complaints.csv')
Rows: 42413 Columns: 5
── Column specification ────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): Animal Type, Complaint Type, Date Received, Suburb, Electoral Division

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
brisbane_complaints <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-21/brisbane_complaints.csv')
Rows: 31330 Columns: 7
── Column specification ────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (7): nature, animal_type, category, suburb, date_range, responsible_office, city

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

1.5 EDA

head(animal_outcomes)
animal_outcomes %>% glimpse()
Rows: 664
Columns: 12
$ year        <dbl> 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 19…
$ animal_type <chr> "Dogs", "Dogs", "Dogs", "Dogs", "Cats", "Cats", "Cats", "Cats", "Horses", "Horses", "H…
$ outcome     <chr> "Reclaimed", "Rehomed", "Other", "Euthanized", "Reclaimed", "Rehomed", "Other", "Eutha…
$ ACT         <dbl> 610, 1245, 12, 360, 111, 1442, 0, 1007, 0, 1, 0, 0, 2, 90, 39, 9, 390, 173, 746, 31, 2…
$ NSW         <dbl> 3140, 7525, 745, 9221, 201, 3913, 447, 8205, 0, 12, 0, 8, 15, 719, 15, 49, 26, 597, 18…
$ NT          <dbl> 205, 526, 955, 9, 22, 269, 0, 847, 1, 3, 0, 0, 0, 120, 0, 0, 6, 32, 50, 5, 60, 0, 10, …
$ QLD         <dbl> 1392, 5489, 860, 9214, 206, 3901, 386, 10554, 0, 3, 11, 1, 9, 88, 217, 109, 1461, 0, 1…
$ SA          <dbl> 2329, 1105, 380, 1701, 157, 1055, 46, 3415, 2, 10, 1, 0, 13, 61, 2, 54, 175, 446, 861,…
$ TAS         <dbl> 516, 480, 168, 599, 31, 752, 124, 1056, 1, 0, 2, 0, 1, 25, 6, 2, 66, 127, 75, 5, 63, 2…
$ VIC         <dbl> 7130, 4908, 1001, 5217, 884, 3768, 1501, 6113, 87, 19, 0, 4, 418, 315, 18, 179, 4582, …
$ WA          <dbl> 1, 137, 6, 18, 0, 62, 5, 5, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 12, 1, 456, 755, 94…
$ Total       <dbl> 15323, 21415, 4127, 26339, 1612, 15162, 2509, 31202, 91, 48, 14, 13, 458, 1421, 297, 4…
animal_outcomes %>% count(animal_type)
animal_outcomes %>% count(outcome)
animal_complaints %>%  head()
animal_complaints %>% glimpse()
Rows: 42,413
Columns: 5
$ `Animal Type`        <chr> "dog", "dog", "dog", "dog", "dog", "dog", "dog", "dog", "dog", "dog", "dog", …
$ `Complaint Type`     <chr> "Aggressive Animal", "Noise", "Noise", "Private Impound", "Wandering", "Attac…
$ `Date Received`      <chr> "June 2020", "June 2020", "June 2020", "June 2020", "June 2020", "June 2020",…
$ Suburb               <chr> "Alice River", "Alice River", "Alice River", "Alice River", "Alice River", "B…
$ `Electoral Division` <chr> "Division 1", "Division 1", "Division 1", "Division 1", "Division 1", "Divisi…
animal_complaints %>% n_distinct("Complaint Type")
[1] 16667
animal_complaints %>% summarise_all(n_distinct)
animal_complaints %>% count(`Complaint Type`) 
animal_complaints %>% 
  count(`Complaint Type`) %>% 
  ggplot(aes(x = `Complaint Type`, y = n)) + 
  geom_col()

1.5.1 changing all chars to factors

animal_complaints <- animal_complaints %>% 
  mutate_if(is.character, as.factor)

str(animal_complaints)
tibble [42,413 × 5] (S3: tbl_df/tbl/data.frame)
 $ Animal Type       : Factor w/ 2 levels "cat","dog": 2 2 2 2 2 2 2 2 2 2 ...
 $ Complaint Type    : Factor w/ 6 levels "Aggressive Animal",..: 1 4 4 5 6 2 3 6 3 3 ...
 $ Date Received     : Factor w/ 81 levels "April 2014","April 2015",..: 47 47 47 47 47 47 47 47 47 47 ...
 $ Suburb            : Factor w/ 85 levels "Aitkenvale","Alice River",..: 2 2 2 2 2 10 10 10 11 11 ...
 $ Electoral Division: Factor w/ 11 levels "Division 1","Division 10",..: 1 1 1 1 1 1 1 1 1 1 ...
animal_complaints %>% 
  select(`Complaint Type`, `Electoral Division`) %>% 
  group_by(`Electoral Division`, `Complaint Type`) %>% 
  summarise(counts = n() ) %>% 
  ggplot(aes(`Electoral Division`, counts, fill = `Complaint Type`)) +
  geom_col() +
  theme(axis.text.x = element_text(angle = 90))
`summarise()` has grouped output by 'Electoral Division'. You can override using the `.groups` argument.

animal_complaints %>% 
  select(`Animal Type`, `Electoral Division`) %>% 
  group_by(`Electoral Division`, `Animal Type`) %>% 
  summarise(counts = n() ) %>% 
  ggplot(aes(`Electoral Division`, counts, fill = `Animal Type`)) +
  geom_col() +
  theme(axis.text.x = element_text(angle = 90))
`summarise()` has grouped output by 'Electoral Division'. You can override using the `.groups` argument.

animal_complaints %>% 
  select(`Animal Type`, `Complaint Type`) %>% 
  group_by(`Complaint Type`, `Animal Type`) %>% 
  summarise(counts = n() ) %>% 
  ggplot(aes(`Complaint Type`, counts, fill = `Animal Type`)) +
  geom_col() +
  theme(axis.text.x = element_text(angle = 90))
`summarise()` has grouped output by 'Complaint Type'. You can override using the `.groups` argument.

1.6 Functions

1.6.1 Renaming column names

animal_complaints <- animal_complaints %>% 
  rename_all(.funs = function(.x){
    .x %>% tolower() %>% str_replace(pattern = " ", replacement = "_")
  })
animal_complaints %>% head()

1.6.2 Convert_to_fraction

animal_outcomes %>%  head()
convert_to_frac <- function(var, total){
  return(var / total)
}

animal_outcomes %>% 
  mutate(across(ACT:WA, ~convert_to_frac(var = .x, total = Total )))
NA

1.6.3 Calling udf inside udf

convert_to_frac_df <- function(df) {
  
  df %>% 
  mutate(across(ACT:WA, ~convert_to_frac(var = .x, total = Total )))
}

convert_to_frac_df(animal_outcomes)
animal_outcomes %>% convert_to_frac_df()

1.6.4 Another way of above function

use . instead of df

tiday_frac <- . %>% mutate(across(ACT:WA, ~convert_to_frac(var = .x, total = Total )))

animal_outcomes %>% tiday_frac()

1.6.5 factors_bar_chart

animal_outcomes %>% 
  select(outcome) %>% 
  count(outcome) %>% 
  mutate(outcome = reorder(outcome, n)) %>% 
  ggplot(aes(x = outcome, y = n, fill = outcome)) +
  geom_col() +
  theme_bw() +
  coord_flip()

factors_bar_chart <- function(df, var){
  var <- enquo(var)
  
  df %>% 
    select(!!var) %>% 
    count(!!var) %>% 
    mutate(!!var := reorder(!!var, n)) %>% 
    ggplot(aes(x = !!var, y = n, fill = !!var)) +
    geom_col() +
    theme_bw() +
    coord_flip()
}

factors_bar_chart(animal_outcomes, outcome)

factors_bar_chart(animal_outcomes, animal_type)

1.7 Functions for Analysing each county

brisbane_complaints %>%  glimpse()
Rows: 31,330
Columns: 7
$ nature             <chr> "Animal", "Animal", "Animal", "Animal", "Animal", "Animal", "Animal", "Animal",…
$ animal_type        <chr> "Dog", "Dog", "Dog", "Dog", "Attack", "Attack", "Dog", "Attack", "Dog", "Dog", …
$ category           <chr> "Fencing Issues", "Fencing Issues", "Defecating In Public", "Fencing Issues", "…
$ suburb             <chr> "SUNNYBANK", "SUNNYBANK HILLS", "SUNNYBANK", "SUNNYBANK", "CALAMVALE", "STRETTO…
$ date_range         <chr> "1st-quarter-2016-17.csv", "1st-quarter-2016-17.csv", "1st-quarter-2016-17.csv"…
$ responsible_office <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ city               <chr> "Brisbane", "Brisbane", "Brisbane", "Brisbane", "Brisbane", "Brisbane", "Brisba…

1.7.1 columns unique value count

brisbane_complaints %>% map_dbl(~n_distinct(.x))
            nature        animal_type           category             suburb         date_range 
                 1                  5                 24                192                 17 
responsible_office               city 
                 9                  1 

1.7.2 converting char to factors

brisbane_complaints <- brisbane_complaints %>% 
                          mutate_if(is.character, as.factor)

str(brisbane_complaints)
tibble [31,330 × 7] (S3: tbl_df/tbl/data.frame)
 $ nature            : Factor w/ 1 level "Animal": 1 1 1 1 1 1 1 1 1 1 ...
 $ animal_type       : Factor w/ 5 levels "Attack","Cat",..: 4 4 4 4 1 1 4 1 4 4 ...
 $ category          : Factor w/ 23 levels "Attack On A Person",..: 7 7 5 7 2 1 NA 2 7 5 ...
 $ suburb            : Factor w/ 191 levels "ACACIA RIDGE",..: 162 163 162 162 29 160 5 63 73 73 ...
 $ date_range        : Factor w/ 17 levels "1st-quarter-2016-17.csv",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ responsible_office: Factor w/ 8 levels "City Safety (Animal Management)",..: NA NA NA NA NA NA NA NA NA NA ...
 $ city              : Factor w/ 1 level "Brisbane": 1 1 1 1 1 1 1 1 1 1 ...

1.7.3 Chart for 1 Suburb

brisbane_complaints %>% 
  filter(suburb == "SUNNYBANK") %>% 
  count(category) %>% 
  drop_na() %>% 
  mutate(category = reorder(category, n)) %>% 
  
  ggplot(aes(x = category, y =n, fill = category)) +
  geom_col() +
  coord_flip() +
  theme_bw()

brisbane_complaints %>% 
  filter(suburb == "SUNNYBANK",
         animal_type == "Attack") %>% 
  count(category) %>% 
  drop_na() %>% 
  mutate(category = reorder(category, n)) %>% 
  
  ggplot(aes(x = category, y =n, fill = category)) +
  geom_col() +
  coord_flip() +
  theme_bw()

1.7.4 Function for charting all suburbs

brisbane_complaints %>% 
  filter(animal_type == "Attack") %>% 
  count(suburb, category) %>% 
  drop_na() %>% 
  mutate(category = reorder(category, n)) 

save_charts_func <- function(df, filename){
  
  temp_chart <- df %>% 
                  mutate(category = reorder(category, n)) %>%
                  ggplot(aes(x = category, y =n, fill = category)) +
                    geom_col() +
                    coord_flip() +
                    theme_bw() +
                    ggtitle(paste0(filename,"Attacks"))
                            
  ggsave(filename = paste0(filename, ".pdf"), 
         plot = temp_chart, 
         width = 11, height = 8.5, units = "in")
}
brisbane_complaints %>% 
  filter(animal_type == "Attack") %>% 
  count(suburb, category) %>% 
  drop_na() %>% 
  nest(-suburb)

1.7.4.1 Applying function to save charts


library(magrittr)

Attaching package: ‘magrittr’

The following object is masked from ‘package:purrr’:

    set_names

The following object is masked from ‘package:tidyr’:

    extract
brisbane_complaints %>% 
  filter(animal_type == "Attack") %>% 
  count(suburb, category) %>% 
  drop_na() %>% 
  nest(-suburb) %>% 
  mutate(suburb = str_replace(suburb, " ","_")) %$% 
  walk2(.x = data, .y = suburb, .f = save_charts_func)

Another way of saving charts

from: https://youtu.be/GxvccD8K49M?t=3262 (About Functional Programming, Purr package)

# dir.create("charts_images")

save_charts_func2 <- function(df, filename){
  
  temp_chart <- df %>% 
                  mutate(category = reorder(category, n)) %>%
                  ggplot(aes(x = category, y =n, fill = category)) +
                    geom_col() +
                    coord_flip() +
                    theme_bw() +
                    ggtitle(paste0(filename,"Attacks"))
                            
  ggsave(filename = paste0("charts_images/",filename, ".png"), 
         plot = temp_chart, 
         width = 11, height = 8.5, units = "in")
}
brisbane_complaints %>% 
  filter(animal_type == "Attack") %>% 
  count(suburb, category) %>% 
  drop_na() %>% 
  nest(-suburb) %>% 
  mutate(suburb = str_replace(suburb, " ","_")) %$%
  walk2(.x = data, .y = suburb, .f = save_charts_func2)

1.8 NHSR data set

from: https://youtu.be/GxvccD8K49M?t=2832

# install.packages("NHSRdatasets")
library(NHSRdatasets)
ae_attendances %>% head()
ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>% head()
ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances))
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest()
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
ae_attendances %>% 
  filter(str_starts(org_code, "R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest()
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest()
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
.Last.value$data[[1]]
ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest() %>% 
  filter(map_dbl(data, nrow) == 36)
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.

1.8.1 udf function to plot

plot_fn <- function(org_code, data){
  data %>%
    ggplot(aes(period, attendances)) +
    geom_line() +
    geom_point() +
    labs(title = org_code) +
    theme_bw() +
    theme(axis.text.x = element_text(angle = 90))
} 
  

1.8.2 Creating plots using map

ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest() %>% 
  filter(map_dbl(data, nrow) == 36) %>% 
  mutate(plot = map2(.x = org_code, .y = data, .f = plot_fn))
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.

1.8.3 Saving plots automatically

# dir.create("nhsr_charts")


ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest() %>% 
  filter(map_dbl(data, nrow) == 36) %>% 
  
  # creating plot
  mutate(plot = map2(.x = org_code, .y = data, .f = plot_fn)) %>%
  
  # creating file names
  mutate(filename = paste0("nhsr_charts/", org_code, ".png")) 
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
# dir.create("nhsr_charts")


ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest() %>% 
  filter(map_dbl(data, nrow) == 36) %>% 
  
  # creating plot
  mutate(plot = map2(.x = org_code, .y = data, .f = plot_fn)) %>%
  
  # creating file names
  mutate(filename = paste0("nhsr_charts/", org_code, ".png")) %>% 
  
  ungroup() %>% 
  
  # selecting only plots column to save plots
  head(10) %>% 
  select(plot, filename) %>% 
  
  #saving plots
  pwalk(ggsave)
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
# dir.create("nhsr_charts2")

library(magrittr)

ae_attendances %>% 
  filter(org_code %>% str_starts("R")) %>%
  group_by(org_code, period) %>% 
  summarise(attendances = sum(attendances)) %>% 
  nest() %>% 
  filter(map_dbl(data, nrow) == 36) %>% 
  
  # creating plot
  mutate(plot_var = map2(.x = org_code, .y = data, .f = plot_fn)) %>%
  
  # creating file names
  mutate(filename = paste0("nhsr_charts2/", org_code, ".png")) %>% 
  
  ungroup() %>% 
  
  # selecting only plots column to save plots
  head(10) %>% 
  select(plot_var, filename) %$% 
  
  #saving plots
  walk2(.x = filename, .y = plot_var, .f = ggsave)
`summarise()` has grouped output by 'org_code'. You can override using the `.groups` argument.
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
Saving 7 x 7 in image
LS0tDQp0aXRsZTogIlVzZXIgRGVmaW5lZCBGdW5jdGlvbnMgdGlkeXR1ZXNkYXkiDQpvdXRwdXQ6ICAgDQogIGh0bWxfbm90ZWJvb2s6DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogDQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNg0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmJvZHksIHRkIHsNCiAgIGZvbnQtZmFtaWx5OiAiT0NSLUIgMTAgQlQiOw0KfQ0KY29kZS5yew0KICBmb250LWZhbWlseTogIk9DUi1CIDEwIEJUIjsNCn0NCnByZSB7DQogIGZvbnQtZmFtaWx5OiAiT0NSLUIgMTAgQlQiOw0KfQ0KPC9zdHlsZT4NCg0KIyBGdW5jdGlvbnMgdGlkeXR1ZXNkYXkNCg0KDQojIyBvcHRpb25zICYgc2V0dGluZ3MNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQ0KYGBgDQoNCg0KYGBge3J9DQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCmBgYA0KDQoNCiMjIGxpbmtzDQoNCmZyb206IA0KDQpodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PTdvejFxR0NscmwwDQpodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL2RhdGEvMjAyMC8yMDIwLTA3LTIxDQoNCg0KIyMgaW5zdGFsbCBsaWJzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIyMgbG9hZCBkYXRhDQoNCmBgYHtyfQ0KYW5pbWFsX291dGNvbWVzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMDctMjEvYW5pbWFsX291dGNvbWVzLmNzdicpDQoNCmFuaW1hbF9jb21wbGFpbnRzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMDctMjEvYW5pbWFsX2NvbXBsYWludHMuY3N2JykNCg0KYnJpc2JhbmVfY29tcGxhaW50cyA8LSByZWFkcjo6cmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMC8yMDIwLTA3LTIxL2JyaXNiYW5lX2NvbXBsYWludHMuY3N2JykNCg0KYGBgDQoNCg0KIyMgRURBDQoNCmBgYHtyfQ0KaGVhZChhbmltYWxfb3V0Y29tZXMpDQpgYGANCg0KYGBge3J9DQphbmltYWxfb3V0Y29tZXMgJT4lIGdsaW1wc2UoKQ0KYGBgDQoNCmBgYHtyfQ0KYW5pbWFsX291dGNvbWVzICU+JSBjb3VudChhbmltYWxfdHlwZSkNCmBgYA0KDQoNCmBgYHtyfQ0KYW5pbWFsX291dGNvbWVzICU+JSBjb3VudChvdXRjb21lKQ0KYGBgDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgJT4lICBoZWFkKCkNCmBgYA0KDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgJT4lIGdsaW1wc2UoKQ0KYGBgDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgJT4lIG5fZGlzdGluY3QoIkNvbXBsYWludCBUeXBlIikNCmBgYA0KDQpgYGB7cn0NCmFuaW1hbF9jb21wbGFpbnRzICU+JSBzdW1tYXJpc2VfYWxsKG5fZGlzdGluY3QpDQpgYGANCg0KYGBge3J9DQphbmltYWxfY29tcGxhaW50cyAlPiUgY291bnQoYENvbXBsYWludCBUeXBlYCkgDQpgYGANCg0KYGBge3J9DQphbmltYWxfY29tcGxhaW50cyAlPiUgDQogIGNvdW50KGBDb21wbGFpbnQgVHlwZWApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYENvbXBsYWludCBUeXBlYCwgeSA9IG4pKSArIA0KICBnZW9tX2NvbCgpDQoNCmBgYA0KDQojIyMgY2hhbmdpbmcgYWxsIGNoYXJzIHRvIGZhY3RvcnMNCg0KYGBge3J9DQphbmltYWxfY29tcGxhaW50cyA8LSBhbmltYWxfY29tcGxhaW50cyAlPiUgDQogIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLmZhY3RvcikNCg0Kc3RyKGFuaW1hbF9jb21wbGFpbnRzKQ0KYGBgDQoNCg0KYGBge3J9DQphbmltYWxfY29tcGxhaW50cyAlPiUgDQogIHNlbGVjdChgQ29tcGxhaW50IFR5cGVgLCBgRWxlY3RvcmFsIERpdmlzaW9uYCkgJT4lIA0KICBncm91cF9ieShgRWxlY3RvcmFsIERpdmlzaW9uYCwgYENvbXBsYWludCBUeXBlYCkgJT4lIA0KICBzdW1tYXJpc2UoY291bnRzID0gbigpICkgJT4lIA0KICBnZ3Bsb3QoYWVzKGBFbGVjdG9yYWwgRGl2aXNpb25gLCBjb3VudHMsIGZpbGwgPSBgQ29tcGxhaW50IFR5cGVgKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgJT4lIA0KICBzZWxlY3QoYEFuaW1hbCBUeXBlYCwgYEVsZWN0b3JhbCBEaXZpc2lvbmApICU+JSANCiAgZ3JvdXBfYnkoYEVsZWN0b3JhbCBEaXZpc2lvbmAsIGBBbmltYWwgVHlwZWApICU+JSANCiAgc3VtbWFyaXNlKGNvdW50cyA9IG4oKSApICU+JSANCiAgZ2dwbG90KGFlcyhgRWxlY3RvcmFsIERpdmlzaW9uYCwgY291bnRzLCBmaWxsID0gYEFuaW1hbCBUeXBlYCkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmFuaW1hbF9jb21wbGFpbnRzICU+JSANCiAgc2VsZWN0KGBBbmltYWwgVHlwZWAsIGBDb21wbGFpbnQgVHlwZWApICU+JSANCiAgZ3JvdXBfYnkoYENvbXBsYWludCBUeXBlYCwgYEFuaW1hbCBUeXBlYCkgJT4lIA0KICBzdW1tYXJpc2UoY291bnRzID0gbigpICkgJT4lIA0KICBnZ3Bsb3QoYWVzKGBDb21wbGFpbnQgVHlwZWAsIGNvdW50cywgZmlsbCA9IGBBbmltYWwgVHlwZWApKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkNCg0KYGBgDQoNCiMjIEZ1bmN0aW9ucw0KDQojIyMgUmVuYW1pbmcgY29sdW1uIG5hbWVzDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgPC0gYW5pbWFsX2NvbXBsYWludHMgJT4lIA0KICByZW5hbWVfYWxsKC5mdW5zID0gZnVuY3Rpb24oLngpew0KICAgIC54ICU+JSB0b2xvd2VyKCkgJT4lIHN0cl9yZXBsYWNlKHBhdHRlcm4gPSAiICIsIHJlcGxhY2VtZW50ID0gIl8iKQ0KICB9KQ0KYGBgDQoNCmBgYHtyfQ0KYW5pbWFsX2NvbXBsYWludHMgJT4lIGhlYWQoKQ0KYGBgDQoNCg0KIyMjIENvbnZlcnRfdG9fZnJhY3Rpb24NCg0KYGBge3J9DQphbmltYWxfb3V0Y29tZXMgJT4lICBoZWFkKCkNCmBgYA0KDQoNCmBgYHtyfQ0KY29udmVydF90b19mcmFjIDwtIGZ1bmN0aW9uKHZhciwgdG90YWwpew0KICByZXR1cm4odmFyIC8gdG90YWwpDQp9DQoNCmFuaW1hbF9vdXRjb21lcyAlPiUgDQogIG11dGF0ZShhY3Jvc3MoQUNUOldBLCB+Y29udmVydF90b19mcmFjKHZhciA9IC54LCB0b3RhbCA9IFRvdGFsICkpKQ0KDQpgYGANCg0KDQojIyMgQ2FsbGluZyB1ZGYgaW5zaWRlIHVkZiANCg0KYGBge3J9DQpjb252ZXJ0X3RvX2ZyYWNfZGYgPC0gZnVuY3Rpb24oZGYpIHsNCiAgDQogIGRmICU+JSANCiAgbXV0YXRlKGFjcm9zcyhBQ1Q6V0EsIH5jb252ZXJ0X3RvX2ZyYWModmFyID0gLngsIHRvdGFsID0gVG90YWwgKSkpDQp9DQoNCmNvbnZlcnRfdG9fZnJhY19kZihhbmltYWxfb3V0Y29tZXMpDQpgYGANCg0KDQpgYGB7cn0NCmFuaW1hbF9vdXRjb21lcyAlPiUgY29udmVydF90b19mcmFjX2RmKCkNCmBgYA0KDQoNCiMjIyBBbm90aGVyIHdheSBvZiBhYm92ZSBmdW5jdGlvbg0KDQp1c2UgLiBpbnN0ZWFkIG9mIGRmDQoNCmBgYHtyfQ0KdGlkYXlfZnJhYyA8LSAuICU+JSBtdXRhdGUoYWNyb3NzKEFDVDpXQSwgfmNvbnZlcnRfdG9fZnJhYyh2YXIgPSAueCwgdG90YWwgPSBUb3RhbCApKSkNCg0KYW5pbWFsX291dGNvbWVzICU+JSB0aWRheV9mcmFjKCkNCmBgYA0KDQoNCiMjIyBmYWN0b3JzX2Jhcl9jaGFydA0KDQpgYGB7cn0NCmFuaW1hbF9vdXRjb21lcyAlPiUgDQogIHNlbGVjdChvdXRjb21lKSAlPiUgDQogIGNvdW50KG91dGNvbWUpICU+JSANCiAgbXV0YXRlKG91dGNvbWUgPSByZW9yZGVyKG91dGNvbWUsIG4pKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IG91dGNvbWUsIHkgPSBuLCBmaWxsID0gb3V0Y29tZSkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lX2J3KCkgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQpgYGB7cn0NCmZhY3RvcnNfYmFyX2NoYXJ0IDwtIGZ1bmN0aW9uKGRmLCB2YXIpew0KICB2YXIgPC0gZW5xdW8odmFyKQ0KICANCiAgZGYgJT4lIA0KICAgIHNlbGVjdCghIXZhcikgJT4lIA0KICAgIGNvdW50KCEhdmFyKSAlPiUgDQogICAgbXV0YXRlKCEhdmFyIDo9IHJlb3JkZXIoISF2YXIsIG4pKSAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gISF2YXIsIHkgPSBuLCBmaWxsID0gISF2YXIpKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgdGhlbWVfYncoKSArDQogICAgY29vcmRfZmxpcCgpDQp9DQoNCmZhY3RvcnNfYmFyX2NoYXJ0KGFuaW1hbF9vdXRjb21lcywgb3V0Y29tZSkNCmBgYA0KDQpgYGB7cn0NCmZhY3RvcnNfYmFyX2NoYXJ0KGFuaW1hbF9vdXRjb21lcywgYW5pbWFsX3R5cGUpDQpgYGANCg0KIyMgRnVuY3Rpb25zIGZvciBBbmFseXNpbmcgZWFjaCBjb3VudHkNCg0KYGBge3J9DQpicmlzYmFuZV9jb21wbGFpbnRzICU+JSAgZ2xpbXBzZSgpDQpgYGANCg0KIyMjIGNvbHVtbnMgdW5pcXVlIHZhbHVlIGNvdW50DQoNCmBgYHtyfQ0KYnJpc2JhbmVfY29tcGxhaW50cyAlPiUgbWFwX2RibCh+bl9kaXN0aW5jdCgueCkpDQpgYGANCg0KIyMjIGNvbnZlcnRpbmcgY2hhciB0byBmYWN0b3JzDQoNCmBgYHtyfQ0KYnJpc2JhbmVfY29tcGxhaW50cyA8LSBicmlzYmFuZV9jb21wbGFpbnRzICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMuZmFjdG9yKQ0KDQpzdHIoYnJpc2JhbmVfY29tcGxhaW50cykNCmBgYA0KDQojIyMgQ2hhcnQgZm9yIDEgU3VidXJiDQoNCmBgYHtyfQ0KYnJpc2JhbmVfY29tcGxhaW50cyAlPiUgDQogIGZpbHRlcihzdWJ1cmIgPT0gIlNVTk5ZQkFOSyIpICU+JSANCiAgY291bnQoY2F0ZWdvcnkpICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgbXV0YXRlKGNhdGVnb3J5ID0gcmVvcmRlcihjYXRlZ29yeSwgbikpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IGNhdGVnb3J5LCB5ID1uLCBmaWxsID0gY2F0ZWdvcnkpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KDQpgYGB7cn0NCmJyaXNiYW5lX2NvbXBsYWludHMgJT4lIA0KICBmaWx0ZXIoc3VidXJiID09ICJTVU5OWUJBTksiLA0KICAgICAgICAgYW5pbWFsX3R5cGUgPT0gIkF0dGFjayIpICU+JSANCiAgY291bnQoY2F0ZWdvcnkpICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgbXV0YXRlKGNhdGVnb3J5ID0gcmVvcmRlcihjYXRlZ29yeSwgbikpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IGNhdGVnb3J5LCB5ID1uLCBmaWxsID0gY2F0ZWdvcnkpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMjIEZ1bmN0aW9uIGZvciBjaGFydGluZyBhbGwgc3VidXJicyANCg0KYGBge3J9DQpicmlzYmFuZV9jb21wbGFpbnRzICU+JSANCiAgZmlsdGVyKGFuaW1hbF90eXBlID09ICJBdHRhY2siKSAlPiUgDQogIGNvdW50KHN1YnVyYiwgY2F0ZWdvcnkpICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgbXV0YXRlKGNhdGVnb3J5ID0gcmVvcmRlcihjYXRlZ29yeSwgbikpIA0KYGBgDQoNCg0KYGBge3J9DQoNCnNhdmVfY2hhcnRzX2Z1bmMgPC0gZnVuY3Rpb24oZGYsIGZpbGVuYW1lKXsNCiAgDQogIHRlbXBfY2hhcnQgPC0gZGYgJT4lIA0KICAgICAgICAgICAgICAgICAgbXV0YXRlKGNhdGVnb3J5ID0gcmVvcmRlcihjYXRlZ29yeSwgbikpICU+JQ0KICAgICAgICAgICAgICAgICAgZ2dwbG90KGFlcyh4ID0gY2F0ZWdvcnksIHkgPW4sIGZpbGwgPSBjYXRlZ29yeSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9jb2woKSArDQogICAgICAgICAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2J3KCkgKw0KICAgICAgICAgICAgICAgICAgICBnZ3RpdGxlKHBhc3RlMChmaWxlbmFtZSwiQXR0YWNrcyIpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoZmlsZW5hbWUsICIucGRmIiksIA0KICAgICAgICAgcGxvdCA9IHRlbXBfY2hhcnQsIA0KICAgICAgICAgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOC41LCB1bml0cyA9ICJpbiIpDQp9DQpgYGANCg0KDQpgYGB7cn0NCmJyaXNiYW5lX2NvbXBsYWludHMgJT4lIA0KICBmaWx0ZXIoYW5pbWFsX3R5cGUgPT0gIkF0dGFjayIpICU+JSANCiAgY291bnQoc3VidXJiLCBjYXRlZ29yeSkgJT4lIA0KICBkcm9wX25hKCkgJT4lIA0KICBuZXN0KC1zdWJ1cmIpDQpgYGANCg0KIyMjIyBBcHBseWluZyBmdW5jdGlvbiB0byBzYXZlIGNoYXJ0cw0KDQpgYGB7cn0NCg0KbGlicmFyeShtYWdyaXR0cikNCg0KYnJpc2JhbmVfY29tcGxhaW50cyAlPiUgDQogIGZpbHRlcihhbmltYWxfdHlwZSA9PSAiQXR0YWNrIikgJT4lIA0KICBjb3VudChzdWJ1cmIsIGNhdGVnb3J5KSAlPiUgDQogIGRyb3BfbmEoKSAlPiUgDQogIG5lc3QoLXN1YnVyYikgJT4lIA0KICBtdXRhdGUoc3VidXJiID0gc3RyX3JlcGxhY2Uoc3VidXJiLCAiICIsIl8iKSkgJSQlIA0KICB3YWxrMigueCA9IGRhdGEsIC55ID0gc3VidXJiLCAuZiA9IHNhdmVfY2hhcnRzX2Z1bmMpDQpgYGANCg0KQW5vdGhlciB3YXkgb2Ygc2F2aW5nIGNoYXJ0cw0KDQpmcm9tOiBodHRwczovL3lvdXR1LmJlL0d4dmNjRDhLNDlNP3Q9MzI2MiAoQWJvdXQgRnVuY3Rpb25hbCBQcm9ncmFtbWluZywgUHVyciBwYWNrYWdlKQ0KDQpgYGB7cn0NCiMgZGlyLmNyZWF0ZSgiY2hhcnRzX2ltYWdlcyIpDQpgYGANCg0KDQpgYGB7cn0NCg0Kc2F2ZV9jaGFydHNfZnVuYzIgPC0gZnVuY3Rpb24oZGYsIGZpbGVuYW1lKXsNCiAgDQogIHRlbXBfY2hhcnQgPC0gZGYgJT4lIA0KICAgICAgICAgICAgICAgICAgbXV0YXRlKGNhdGVnb3J5ID0gcmVvcmRlcihjYXRlZ29yeSwgbikpICU+JQ0KICAgICAgICAgICAgICAgICAgZ2dwbG90KGFlcyh4ID0gY2F0ZWdvcnksIHkgPW4sIGZpbGwgPSBjYXRlZ29yeSkpICsNCiAgICAgICAgICAgICAgICAgICAgZ2VvbV9jb2woKSArDQogICAgICAgICAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICAgICAgICAgIHRoZW1lX2J3KCkgKw0KICAgICAgICAgICAgICAgICAgICBnZ3RpdGxlKHBhc3RlMChmaWxlbmFtZSwiQXR0YWNrcyIpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoImNoYXJ0c19pbWFnZXMvIixmaWxlbmFtZSwgIi5wbmciKSwgDQogICAgICAgICBwbG90ID0gdGVtcF9jaGFydCwgDQogICAgICAgICB3aWR0aCA9IDExLCBoZWlnaHQgPSA4LjUsIHVuaXRzID0gImluIikNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KYnJpc2JhbmVfY29tcGxhaW50cyAlPiUgDQogIGZpbHRlcihhbmltYWxfdHlwZSA9PSAiQXR0YWNrIikgJT4lIA0KICBjb3VudChzdWJ1cmIsIGNhdGVnb3J5KSAlPiUgDQogIGRyb3BfbmEoKSAlPiUgDQogIG5lc3QoLXN1YnVyYikgJT4lIA0KICBtdXRhdGUoc3VidXJiID0gc3RyX3JlcGxhY2Uoc3VidXJiLCAiICIsIl8iKSkgJSQlDQogIHdhbGsyKC54ID0gZGF0YSwgLnkgPSBzdWJ1cmIsIC5mID0gc2F2ZV9jaGFydHNfZnVuYzIpDQpgYGANCg0KDQojIyBOSFNSIGRhdGEgc2V0DQoNCmZyb206IA0KaHR0cHM6Ly95b3V0dS5iZS9HeHZjY0Q4SzQ5TT90PTI4MzINCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoIk5IU1JkYXRhc2V0cyIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KE5IU1JkYXRhc2V0cykNCmBgYA0KDQoNCmBgYHtyfQ0KYWVfYXR0ZW5kYW5jZXMgJT4lIGhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0KYWVfYXR0ZW5kYW5jZXMgJT4lIA0KICBmaWx0ZXIob3JnX2NvZGUgJT4lIHN0cl9zdGFydHMoIlIiKSkgJT4lIGhlYWQoKQ0KYGBgDQoNCg0KYGBge3J9DQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihvcmdfY29kZSAlPiUgc3RyX3N0YXJ0cygiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKQ0KYGBgDQoNCg0KYGBge3J9DQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihvcmdfY29kZSAlPiUgc3RyX3N0YXJ0cygiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKSAlPiUgDQogIG5lc3QoKQ0KYGBgDQoNCg0KYGBge3J9DQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihzdHJfc3RhcnRzKG9yZ19jb2RlLCAiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKSAlPiUgDQogIG5lc3QoKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmFlX2F0dGVuZGFuY2VzICU+JSANCiAgZmlsdGVyKG9yZ19jb2RlICU+JSBzdHJfc3RhcnRzKCJSIikpICU+JQ0KICBncm91cF9ieShvcmdfY29kZSwgcGVyaW9kKSAlPiUgDQogIHN1bW1hcmlzZShhdHRlbmRhbmNlcyA9IHN1bShhdHRlbmRhbmNlcykpICU+JSANCiAgbmVzdCgpDQpgYGANCg0KDQpgYGB7cn0NCi5MYXN0LnZhbHVlJGRhdGFbWzFdXQ0KYGBgDQoNCg0KYGBge3J9DQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihvcmdfY29kZSAlPiUgc3RyX3N0YXJ0cygiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKSAlPiUgDQogIG5lc3QoKSAlPiUgDQogIGZpbHRlcihtYXBfZGJsKGRhdGEsIG5yb3cpID09IDM2KQ0KYGBgDQoNCg0KIyMjIHVkZiBmdW5jdGlvbiB0byBwbG90DQoNCmBgYHtyfQ0KcGxvdF9mbiA8LSBmdW5jdGlvbihvcmdfY29kZSwgZGF0YSl7DQogIGRhdGEgJT4lDQogICAgZ2dwbG90KGFlcyhwZXJpb2QsIGF0dGVuZGFuY2VzKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGxhYnModGl0bGUgPSBvcmdfY29kZSkgKw0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KfSANCiAgDQpgYGANCg0KIyMjIENyZWF0aW5nIHBsb3RzIHVzaW5nIG1hcA0KDQpgYGB7cn0NCmFlX2F0dGVuZGFuY2VzICU+JSANCiAgZmlsdGVyKG9yZ19jb2RlICU+JSBzdHJfc3RhcnRzKCJSIikpICU+JQ0KICBncm91cF9ieShvcmdfY29kZSwgcGVyaW9kKSAlPiUgDQogIHN1bW1hcmlzZShhdHRlbmRhbmNlcyA9IHN1bShhdHRlbmRhbmNlcykpICU+JSANCiAgbmVzdCgpICU+JSANCiAgZmlsdGVyKG1hcF9kYmwoZGF0YSwgbnJvdykgPT0gMzYpICU+JSANCiAgbXV0YXRlKHBsb3QgPSBtYXAyKC54ID0gb3JnX2NvZGUsIC55ID0gZGF0YSwgLmYgPSBwbG90X2ZuKSkNCmBgYA0KDQojIyMgU2F2aW5nIHBsb3RzIGF1dG9tYXRpY2FsbHkNCg0KYGBge3J9DQojIGRpci5jcmVhdGUoIm5oc3JfY2hhcnRzIikNCg0KDQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihvcmdfY29kZSAlPiUgc3RyX3N0YXJ0cygiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKSAlPiUgDQogIG5lc3QoKSAlPiUgDQogIGZpbHRlcihtYXBfZGJsKGRhdGEsIG5yb3cpID09IDM2KSAlPiUgDQogIA0KICAjIGNyZWF0aW5nIHBsb3QNCiAgbXV0YXRlKHBsb3QgPSBtYXAyKC54ID0gb3JnX2NvZGUsIC55ID0gZGF0YSwgLmYgPSBwbG90X2ZuKSkgJT4lDQogIA0KICAjIGNyZWF0aW5nIGZpbGUgbmFtZXMNCiAgbXV0YXRlKGZpbGVuYW1lID0gcGFzdGUwKCJuaHNyX2NoYXJ0cy8iLCBvcmdfY29kZSwgIi5wbmciKSkgDQpgYGANCg0KDQpgYGB7cn0NCiMgZGlyLmNyZWF0ZSgibmhzcl9jaGFydHMiKQ0KDQoNCmFlX2F0dGVuZGFuY2VzICU+JSANCiAgZmlsdGVyKG9yZ19jb2RlICU+JSBzdHJfc3RhcnRzKCJSIikpICU+JQ0KICBncm91cF9ieShvcmdfY29kZSwgcGVyaW9kKSAlPiUgDQogIHN1bW1hcmlzZShhdHRlbmRhbmNlcyA9IHN1bShhdHRlbmRhbmNlcykpICU+JSANCiAgbmVzdCgpICU+JSANCiAgZmlsdGVyKG1hcF9kYmwoZGF0YSwgbnJvdykgPT0gMzYpICU+JSANCiAgDQogICMgY3JlYXRpbmcgcGxvdA0KICBtdXRhdGUocGxvdCA9IG1hcDIoLnggPSBvcmdfY29kZSwgLnkgPSBkYXRhLCAuZiA9IHBsb3RfZm4pKSAlPiUNCiAgDQogICMgY3JlYXRpbmcgZmlsZSBuYW1lcw0KICBtdXRhdGUoZmlsZW5hbWUgPSBwYXN0ZTAoIm5oc3JfY2hhcnRzLyIsIG9yZ19jb2RlLCAiLnBuZyIpKSAlPiUgDQogIA0KICB1bmdyb3VwKCkgJT4lIA0KICANCiAgIyBzZWxlY3Rpbmcgb25seSBwbG90cyBjb2x1bW4gdG8gc2F2ZSBwbG90cw0KICBoZWFkKDEwKSAlPiUgDQogIHNlbGVjdChwbG90LCBmaWxlbmFtZSkgJT4lIA0KICANCiAgI3NhdmluZyBwbG90cw0KICBwd2FsayhnZ3NhdmUpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBkaXIuY3JlYXRlKCJuaHNyX2NoYXJ0czIiKQ0KDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KDQphZV9hdHRlbmRhbmNlcyAlPiUgDQogIGZpbHRlcihvcmdfY29kZSAlPiUgc3RyX3N0YXJ0cygiUiIpKSAlPiUNCiAgZ3JvdXBfYnkob3JnX2NvZGUsIHBlcmlvZCkgJT4lIA0KICBzdW1tYXJpc2UoYXR0ZW5kYW5jZXMgPSBzdW0oYXR0ZW5kYW5jZXMpKSAlPiUgDQogIG5lc3QoKSAlPiUgDQogIGZpbHRlcihtYXBfZGJsKGRhdGEsIG5yb3cpID09IDM2KSAlPiUgDQogIA0KICAjIGNyZWF0aW5nIHBsb3QNCiAgbXV0YXRlKHBsb3RfdmFyID0gbWFwMigueCA9IG9yZ19jb2RlLCAueSA9IGRhdGEsIC5mID0gcGxvdF9mbikpICU+JQ0KICANCiAgIyBjcmVhdGluZyBmaWxlIG5hbWVzDQogIG11dGF0ZShmaWxlbmFtZSA9IHBhc3RlMCgibmhzcl9jaGFydHMyLyIsIG9yZ19jb2RlLCAiLnBuZyIpKSAlPiUgDQogIA0KICB1bmdyb3VwKCkgJT4lIA0KICANCiAgIyBzZWxlY3Rpbmcgb25seSBwbG90cyBjb2x1bW4gdG8gc2F2ZSBwbG90cw0KICBoZWFkKDEwKSAlPiUgDQogIHNlbGVjdChwbG90X3ZhciwgZmlsZW5hbWUpICUkJSANCiAgDQogICNzYXZpbmcgcGxvdHMNCiAgd2FsazIoLnggPSBmaWxlbmFtZSwgLnkgPSBwbG90X3ZhciwgLmYgPSBnZ3NhdmUpDQpgYGANCg0KDQoNCg0KDQo=